package com.example.demo.service;

import com.example.demo.dto.CreateOrderRequest;
import com.example.demo.dto.OrderDTO;
import com.example.demo.entity.Order;
import com.example.demo.repository.OrderRepository;
import com.example.demo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Order Service
 * 
 * Business logic layer for order operations
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {

    private final OrderRepository orderRepository;
    private final UserRepository userRepository;

    /**
     * Get all orders
     */
    @Transactional(readOnly = true)
    public List<OrderDTO> getAllOrders() {
        log.info("Fetching all orders");
        simulateDelay(150, 350);
        
        List<Order> orders = orderRepository.findAll();
        return orders.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * Get order by ID
     */
    @Transactional(readOnly = true)
    public OrderDTO getOrderById(Long id) {
        log.info("Fetching order with ID: {}", id);
        simulateDelay(50, 150);
        
        Order order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Order not found with id: " + id));
        return convertToDTO(order);
    }

    /**
     * Get orders by user ID
     */
    @Transactional(readOnly = true)
    public List<OrderDTO> getOrdersByUserId(Long userId) {
        log.info("Fetching orders for user ID: {}", userId);
        simulateDelay(100, 250);
        
        // Verify user exists
        if (!userRepository.existsById(userId)) {
            throw new RuntimeException("User not found with id: " + userId);
        }
        
        List<Order> orders = orderRepository.findByUserId(userId);
        return orders.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * Create new order
     */
    @Transactional
    public OrderDTO createOrder(CreateOrderRequest request) {
        log.info("Creating new order for user ID: {}", request.getUserId());
        
        // Verify user exists
        if (!userRepository.existsById(request.getUserId())) {
            throw new RuntimeException("User not found with id: " + request.getUserId());
        }
        
        // Simulate payment processing delay
        simulateDelay(300, 600);
        
        String orderNumber = generateOrderNumber();
        BigDecimal totalAmount = request.getPrice().multiply(new BigDecimal(request.getQuantity()));
        
        Order order = new Order();
        order.setOrderNumber(orderNumber);
        order.setUserId(request.getUserId());
        order.setProductName(request.getProductName());
        order.setQuantity(request.getQuantity());
        order.setPrice(request.getPrice());
        order.setTotalAmount(totalAmount);
        order.setStatus("PENDING");
        
        Order savedOrder = orderRepository.save(order);
        log.info("Order created successfully with ID: {} and order number: {}", 
                savedOrder.getId(), orderNumber);
        
        return convertToDTO(savedOrder);
    }

    /**
     * Update order status
     */
    @Transactional
    public OrderDTO updateOrderStatus(Long id, String status) {
        log.info("Updating order status for ID: {} to {}", id, status);
        
        Order order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Order not found with id: " + id));
        
        simulateDelay(100, 200);
        order.setStatus(status);
        
        Order updatedOrder = orderRepository.save(order);
        log.info("Order status updated successfully for ID: {}", id);
        
        return convertToDTO(updatedOrder);
    }

    /**
     * Cancel order
     */
    @Transactional
    public OrderDTO cancelOrder(Long id) {
        log.info("Cancelling order with ID: {}", id);
        
        Order order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Order not found with id: " + id));
        
        if ("COMPLETED".equals(order.getStatus()) || "CANCELLED".equals(order.getStatus())) {
            throw new RuntimeException("Cannot cancel order in " + order.getStatus() + " status");
        }
        
        simulateDelay(150, 300);
        order.setStatus("CANCELLED");
        
        Order updatedOrder = orderRepository.save(order);
        log.info("Order cancelled successfully with ID: {}", id);
        
        return convertToDTO(updatedOrder);
    }

    /**
     * Complete order
     */
    @Transactional
    public OrderDTO completeOrder(Long id) {
        log.info("Completing order with ID: {}", id);
        
        Order order = orderRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Order not found with id: " + id));
        
        if (!"PENDING".equals(order.getStatus())) {
            throw new RuntimeException("Can only complete orders in PENDING status");
        }
        
        // Simulate order processing
        simulateDelay(200, 400);
        order.setStatus("COMPLETED");
        
        Order updatedOrder = orderRepository.save(order);
        log.info("Order completed successfully with ID: {}", id);
        
        return convertToDTO(updatedOrder);
    }

    /**
     * Get total revenue
     */
    @Transactional(readOnly = true)
    public BigDecimal getTotalRevenue() {
        log.info("Calculating total revenue");
        simulateDelay(100, 200);
        
        BigDecimal revenue = orderRepository.calculateTotalRevenue();
        return revenue != null ? revenue : BigDecimal.ZERO;
    }

    /**
     * Convert Order entity to DTO
     */
    private OrderDTO convertToDTO(Order order) {
        OrderDTO dto = new OrderDTO();
        dto.setId(order.getId());
        dto.setOrderNumber(order.getOrderNumber());
        dto.setUserId(order.getUserId());
        dto.setProductName(order.getProductName());
        dto.setQuantity(order.getQuantity());
        dto.setPrice(order.getPrice());
        dto.setTotalAmount(order.getTotalAmount());
        dto.setStatus(order.getStatus());
        dto.setCreatedAt(order.getCreatedAt());
        return dto;
    }

    /**
     * Generate unique order number
     */
    private String generateOrderNumber() {
        return "ORD-" + System.currentTimeMillis() + "-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
    }

    /**
     * Simulate random delay to mimic database and external service operations
     */
    private void simulateDelay(int minMillis, int maxMillis) {
        try {
            int delay = minMillis + (int) (Math.random() * (maxMillis - minMillis));
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("Delay simulation interrupted", e);
        }
    }
}

